<!-- 公式编辑弹窗 -->
<template>
    <div class="view-formula">
        <table class="formula">
            <tr>
                <td colspan="3">
                    <div class="formula-label">
                        请输入公式
                        <span style="font-size:12px;
                                    padding-left:4px;">
                            (Ctrl+z:后退,Ctrl+y:前进,Ctrl+/:注释,Alt+/:取消注释,Tab:向右缩进,Shift+Tab:向左缩进)
                        </span>
                    </div>
                    <textarea class="form-control" id="code" name="code"></textarea>
                </td>
            </tr>
            <tr>
                <td width="30%">
                    <div class="formula-label">函数名</div>
                    <div class="formula-content">
                        <ul class="snippets-groups">
                            <li v-for="(value, key, index) in filteredCodeSnippetCategories" :key="index">
                                <div class="category nowrap" @click="openCodeList(value)">
                                    <i>图标</i>
                                    <!-- <Iconfont :type="value.isExpanded ? 'icon-icon-test2' : 'icon-icon-test3'"></Iconfont> -->
                                    <span class="list-text">{{ key }}</span>
                                    <span class="badge">({{ value.list.length }})</span>
                                </div>
                                <ul class="snippets-types" v-show="value.isExpanded">
                                    <li class="snippet nowrap" v-for="(codeSnippet, index) in value.list" :key="index"
                                        @click="addCode(codeSnippet)" @mouseover="dataMouseover(codeSnippet)">
                                        <span class="list-text">{{ codeSnippet.name }}</span>
                                    </li>
                                </ul>
                            </li>
                        </ul>
                    </div>
                </td>
                <td width="30%">
                    <div class="formula-label">变量</div>
                    <div class="formula-content">
                        <div style="line-height:30px;color:#999">基础变量</div>
                        <template v-for="item in attrList.filter(x => formulaList.includes(x.id))">
                            <div :key="item.id"
                                v-if="isFormulaLabel(item).formulaFkLableShow || isFormulaLabel(item).formulaNormalLableShow"
                                class="formula-content-item"
                                @click="selectVar(item, null, isFormulaLabel(item).formulaFkLableShow ? getChildAttr(item)[0].id : null)">
                                <!-- <Iconfont :type="getIcon(item.value)" style="margin-right:5px;"></Iconfont> -->
                                <i>图标</i>
                                <span>{{ item.name }}</span>
                                <span style="font-size:12px;color:#3366FF">({{ item.id }})</span>
                                <template
                                    v-if="['select', 'radio', 'checkbox'].includes(item.value.type) && item.configure.value === 2 || ['selector', 'table'].includes(item.value.type)">
                                    <div v-for="(children, cIndex) in getChildAttr(item)" :key="cIndex"
                                        class="formula-content-item-child" @click.stop="selectVar(children, item.id)">
                                        <span></span>
                                        <!-- <Iconfont :type="getIcon(children.value)" style="margin-right:5px;"></Iconfont> -->
                                        <i>图标</i>
                                        <span>{{ children.name }}</span>
                                        <span style="font-size:12px;color:#3366FF">({{ children.id }})</span>
                                    </div>
                                </template>
                            </div>
                        </template>
                        <div style="line-height:30px;color:#999">系统变量</div>
                        <div class="formula-content-item" @click="selectSystemVar('userName')">
                            <span>当前用户</span>
                            <span style="font-size:12px;color:#3366FF">(userName)</span>
                        </div>
                        <div class="formula-content-item" @click="selectSystemVar('userRole')">
                            <span>当前用户角色</span>
                            <span style="font-size:12px;color:#3366FF">(userRole)</span>
                        </div>
                        <div style="line-height:30px;color:#999">自定义变量</div>
                        <div class="formula-content-item" v-for="(item, index) in variableList" :key="index"
                            @click="selectGlobalVar(item)" :title="item.value">
                            <span>{{ item.label }}</span>
                            <span style="font-size:12px;color:#3366FF">({{ item.key }})</span>
                        </div>
                    </div>
                </td>
                <td width="40%">
                    <div class="formula-label">说明</div>
                    <ul class="formula-content formula-info">
                        <li v-for="(item, index) in activeInfo" :key="index"
                            :style="{ paddingLeft: index > 2 ? '42px' : '0' }">{{ item }}</li>
                    </ul>
                </td>
            </tr>
        </table>
        <div class="ivu-modal-footer">
            <ButtonGroup :gutter="15">
                <Button type="primary" @click="handleSave">确定</Button>
                <Button @click="handleCancel">取消</Button>
            </ButtonGroup>
        </div>
    </div>
</template>
<script>
import 'codemirror/theme/base16-light.css'
import 'codemirror/lib/codemirror.css'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/dialog/dialog.css'
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/lint/lint.css'
import 'codemirror/mode/sql/sql' // 语言语法定义文件
import FunctionCode from './functionCode'
// import { fieldTypes } from '@dev_config/page-data'
let CodeMirror = require('codemirror/lib/codemirror')
require('codemirror/addon/edit/matchbrackets')
require('codemirror/addon/selection/active-line')
require('codemirror/addon/fold/foldcode')
require('codemirror/addon/fold/brace-fold')
require('codemirror/addon/fold/comment-fold')
require('codemirror/addon/fold/indent-fold')
require('codemirror/addon/fold/foldgutter')
require('codemirror/addon/lint/lint')
require('codemirror/addon/lint/javascript-lint')
require('codemirror/addon/hint/show-hint')
require('codemirror/mode/javascript/javascript')
require('codemirror/addon/comment/comment')

export default {
    props: {
        attrList: Array,
        value: {
            type: String,
            default: ''
        },
        attrData: {
            type: Object,
            default: () => {
                return {}
            }
        },
        formulaList: {
            type: Array,
            default: () => {
                return []
            }
        },
        tableFieldIn: {
            type: Boolean,
            default: false
        }
    },
    data () {
        return {
            editorOption: { lineNumbers: false },
            activeInfo: [],
            codeList: {},
            filteredCodeSnippetCategories: {},
            inputValue: '',
            toolBar: { isLint: false },
            CodeMirrorEditor: null,
            codeValue: this.value,
            selection: { from: {}, to: {} },
            hintFunctionCode: [],
            hintColumnCode: []
        }
    },
    computed: {
        hintCode () {
            return [].concat(this.hintFunctionCode, this.hintColumnCode)
        },
        dataList () {
            return [
                {
                    objectId: '123',
                    attrList: [
                        {
                            id: 'department',
                            name: '部门',
                            value: {
                                type: 'select'
                            }
                        }
                    ]
                }
            ]
            // return this.$store.getters.dataCustomAndSystem
        },
        variableList () {
            return [
                {
                    value: 'value',
                    label: 'label',
                    key: 'key'
                }
            ]
            // let variableList = this.$store.state.config.variableList
            // if (!variableList) this.$store.dispatch('VariableList')
            // return variableList || []
        }
    },
    methods: {
        isFormulaLabel (item) {
            let formulaFkLableShow = false
            let formulaNormalLableShow = false
            if (
                ((['select', 'radio', 'checkbox'].includes(item.value.type) && item.configure.value === 2) || ['selector', 'table'].includes(item.value.type)) &&
                this.getChildAttr(item).length > 0
            ) {
                formulaFkLableShow = true
            } else if ((['select', 'radio', 'checkbox'].includes(item.value.type) && item.configure.value !== 2) || !['select', 'radio', 'checkbox', 'selector', 'table'].includes(item.value.type)) {
                formulaNormalLableShow = true
            }
            return { formulaFkLableShow, formulaNormalLableShow }
        },
        getChildAttr (item) {
            let sourceObjectId = item.value.type === 'table' ? item.configure.source : item.configure.source_object_id
            let resultColumns = item.value.type === 'table' ? item.configure.display_columns : item.configure.result_columns
            let child = this.dataList.find(x => x.objectId === sourceObjectId)
            let list = []
            if (child) {
                list = child.attrList.filter(x => resultColumns.find(y => y.key === x.id) && !['table', 'file', 'richtext', 'images'].includes(x.value.type) && !['role', 'departmentCode'].includes(x.id))
            }
            return list
        },
        getIcon (value) {
            return ''
            // if (value && value.type && fieldTypes) {
            //     let fieldType = fieldTypes.find(x => x.value === value.type)
            //     return fieldType && fieldType.icon
            // }
        },
        selectVar (node, parentNodeId, childNodeId) {
            if (node.id) {
                if (childNodeId) this.CodeMirrorEditor.replaceSelection(`\${${childNodeId}}`)
                else if (parentNodeId) this.CodeMirrorEditor.replaceSelection(`\${${node.id}}`)
                else if (this.tableFieldIn) this.CodeMirrorEditor.replaceSelection(`\${__p__${node.id}}`)
                else this.CodeMirrorEditor.replaceSelection(`\${${node.id}}`)
                let codeValue = this.CodeMirrorEditor.getValue()
                if ((codeValue.includes('表名') || codeValue.includes('字段名')) && ['select', 'radio', 'selector'].includes(node.value.type) && !node.configure.mult && node.configure.type === 2) {
                    if (codeValue.includes('表名')) codeValue = codeValue.replace('表名', `'${node.configure.source_object_id}'`)
                    if (codeValue.includes('字段名')) codeValue = codeValue.replace('字段名', `'${node.configure.value}'`)
                    this.CodeMirrorEditor.setValue(codeValue)
                }
            }
        },
        selectSystemVar (key) {
            // eslint-disable-next-line no-useless-escape
            this.CodeMirrorEditor.replaceSelection(`\$_SYSTEM_{${key}}`)
        },
        selectGlobalVar (node) {
            // eslint-disable-next-line no-useless-escape
            this.CodeMirrorEditor.replaceSelection(`\$_GLOBAL_{${node.key}}`)
        },
        addCode (val) {
            this.CodeMirrorEditor.replaceSelection(val.code) // 追加内容
        },
        dataMouseover (val) {
            if (val.info) this.activeInfo = val.info
        },
        openCodeList (item) {
            item.isExpanded = !item.isExpanded
        },
        initCode (data) {
            let obj = {}
            for (let i = 0; i < data.length; i++) {
                if (!obj[data[i].category]) {
                    obj[data[i].category] = {}
                    obj[data[i].category].list = []
                    obj[data[i].category].isExpanded = false
                }
                obj[data[i].category].list.push(data[i])
            }
            return obj
        },
        filterCategories (srcCategories, codeKeyword) {
            let filteredCategories = {}
            for (let category in srcCategories) {
                let codeSnippets = srcCategories[category].list
                let filteredCodeSnippets = codeSnippets.filter((codeSnippet) => {
                    return codeSnippet.name.toLowerCase().indexOf(codeKeyword.toLowerCase()) > -1
                })
                filteredCategories[category] = {}
                filteredCategories[category].list = filteredCodeSnippets
                filteredCategories[category].isExpanded = true
            }
            return filteredCategories
        },
        undo () {
            this.CodeMirrorEditor.undo()
        },
        redo () {
            this.CodeMirrorEditor.redo()
        },
        comment () {
            if (typeof this.selection.from.line === 'undefined' || typeof this.selection.to.line === 'undefined') {
                return
            }
            if (this.selection.from.line <= this.selection.to.line) {
                this.CodeMirrorEditor.lineComment(this.selection.from, this.selection.to)
            } else {
                this.CodeMirrorEditor.lineComment(this.selection.to, this.selection.from)
            }
        },
        uncomment () {
            if (typeof this.selection.from.line === 'undefined' || typeof this.selection.to.line === 'undefined') {
                return
            }
            if (this.selection.from.line <= this.selection.to.line) {
                this.CodeMirrorEditor.uncomment(this.selection.from, this.selection.to)
            } else {
                this.CodeMirrorEditor.uncomment(this.selection.to, this.selection.from)
            }
        },
        indentLess () {
            this.CodeMirrorEditor.indentSelection('subtract')
        },
        indentMore () {
            this.CodeMirrorEditor.indentSelection('add')
        },
        indentAuto () {
            this.CodeMirrorEditor.indentSelection('smart')
        },
        collapseAll () {
            let cm = this.CodeMirrorEditor
            cm.operation(() => {
                for (let i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) { cm.foldCode(CodeMirror.Pos(i, 0), null, 'fold') }
            })
        },
        expandAll () {
            let cm = this.CodeMirrorEditor
            cm.operation(() => {
                for (let i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) { cm.foldCode(CodeMirror.Pos(i, 0), null, 'unfold') }
            })
        },

        addIcon (data, type, name) {
            // 给 hint 添加类型 icon
            for (let i = 0; i < data.length; i++) {
                let nativeItem = {
                    text: data[i].code,
                    displayText: data[i].name,
                    className: '',
                    render: (elt, self, data) => {
                        let compEle = document.createElement('div')
                        compEle.innerHTML = `<i class="iconfont ${type}" style='color: red;'></i> ${data.displayText}`
                        elt.appendChild(compEle)
                    }
                }
                this[name].push(nativeItem)
            }
        },

        getHints (cm, option) {
            return new Promise((resolve) => {
                setTimeout(() => {
                    // eslint-disable-next-line no-useless-escape
                    let WORD = /[\w\[\]\"\.$]+/
                    let cursor = cm.getCursor()
                    let curLine = cm.getLine(cursor.line)
                    let start = cursor.ch
                    let end = cursor.ch
                    while (start && WORD.test(curLine.charAt(start - 1))) {
                        --start
                    }
                    let curWord = start !== end && curLine.slice(start, end)
                    let list = []
                    let isDefinedObj = false
                    if (curWord) {
                        if (list.length === 0) {
                            let dotIndex = curWord.lastIndexOf('.')
                            let memberStr = curWord.slice(dotIndex + 1)
                            let comList = this.hintCode
                            for (let j = 0; j < comList.length; j++) {
                                if (comList[j].displayText.toLowerCase().lastIndexOf(memberStr.toLowerCase(), 0) === 0) {
                                    list.push(comList[j])
                                }
                            }
                            isDefinedObj = true
                        }
                    }
                    if (isDefinedObj) {
                        return resolve({
                            list: list,
                            from: CodeMirror.Pos(cursor.line, start),
                            to: CodeMirror.Pos(cursor.line, end)
                        })
                    } else {
                        return resolve({
                            list: list,
                            from: CodeMirror.Pos(cursor.line, start + 1),
                            to: CodeMirror.Pos(cursor.line, end)
                        })
                    }
                }, 100)
            })
        },
        initCursorToEnd () {
            // 初始化光标位置到编辑器结尾
            let lastLineNumber = this.CodeMirrorEditor.lastLine()
            let lastCharNumber = this.CodeMirrorEditor.getLine(lastLineNumber).length
            this.CodeMirrorEditor.setCursor(lastLineNumber, lastCharNumber)
            this.selection.from = this.selection.to = this.CodeMirrorEditor.getCursor()
        },
        initCodeMirror () {
            let myTextarea = document.getElementById('code')
            let _this = this
            this.CodeMirrorEditor = CodeMirror.fromTextArea(myTextarea, {
                value: '',
                mode: 'javascript', // 编辑器语言
                // theme:'base16-light', //不设置主题, 使用默认主题
                lineNumbers: true, // 显示行号
                smartIndent: true,
                autofocus: true,
                autoRefresh: true,
                historyEventDelay: 500,
                indentWithTabs: true,
                tabSize: 4,
                indentUnit: 4,
                line: true,
                matchBrackets: true,
                autoCloseBrackets: true,
                styleActiveLine: true,
                foldGutter: true,
                lint: this.toolBar.isLint,
                gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
                extraKeys: {
                    'Ctrl-Space': 'autocomplete',
                    'Ctrl-/': function () {
                        _this.comment()
                    },
                    'Alt-/': function () {
                        _this.uncomment()
                    },
                    'Shift-Tab': function () {
                        _this.indentLess()
                    }
                }, // 自定义快捷键
                // extraKeys: {"Ctrl": "autocomplete"},//ctrl可以弹出选择项
                hintOptions: {
                    completeSingle: false,
                    alignWithWord: false,
                    hint: this.getHints
                }
            })
            // 当输入框内容发生变化 更新 codeValue 值
            this.CodeMirrorEditor.on('change', (cm) => {
                this.codeValue = cm.getValue()
            })

            this.CodeMirrorEditor.on('cursorActivity', (cm) => {
                this.CodeMirrorEditor.showHint()
                this.selection.to = cm.getCursor()
                if (this.CodeMirrorEditor.getSelection().length === 0) {
                    this.selection.from = cm.getCursor()
                }
            })
            this.CodeMirrorEditor.on('mousedown', (cm) => {
                this.selection.from = cm.getCursor()
            })
        },
        handleSave () {
            this.$emit('handle-save', this.codeValue)
        },
        handleCancel () {
            this.$emit('handle-cancel')
        }
    },
    mounted () {
        this.initCodeMirror()
        this.addIcon(FunctionCode, 'icon-bianliang1', 'hintFunctionCode')
        this.addIcon(this.attrList.map(item => {
            return {
                name: item.id,
                code: `\${${item.id}}`
            }
        }), 'icon-bianliang', 'hintColumnCode')
        this.codeList = this.initCode(FunctionCode)
        this.filteredCodeSnippetCategories = this.codeList
        if (this.value) this.CodeMirrorEditor.setValue(this.value)
        this.initCursorToEnd()
    },
    watch: {
        inputValue (val) {
            this.filteredCodeSnippetCategories = this.filterCategories(this.codeList, val)
        }
    }
}
</script>
<style lang="less">
@prefix-cls: ~"formula";
.view-formula{
    height: 100%;
    overflow-y: overlay;
}
.CodeMirror-hints {
    z-index: 9999;
}

.formula-content-item-child {
    margin-left: 21px;
}

.@{prefix-cls} {

    width: 100%;
    border-collapse: collapse;

    .CodeMirror {
        height: 200px;
    }

    td {
        border-top: solid 1px #DADFE7;
        border-right: solid 1px #DADFE7;
        vertical-align: top;
    }

    tr:first-child td {
        border-top: none;
    }

    tr td:last-child {
        border-right: none;
    }

    &-label {
        background-color: #f0faff;
        padding: 0 10px;
        line-height: 32px;
        position: relative;
    }

    &-content {
        padding: 12px;
        max-height: 300px;
        overflow: auto;

        .ivu-tree>ul>li {
            margin-top: 0;
        }

        &-item {
            cursor: pointer;
        }

    }

    &-info {
        list-style: disc;

        li {
            padding-bottom: 5px;

            &:first-child {
                color: #3366FF;
            }
        }
    }

    .code-func {
        color: #917966;
        font-weight: bold;
    }

    .code-var {
        color: #458383;
    }
}

.snippets-groups {
    li {
        cursor: pointer;
    }

    .snippet {
        text-indent: 2em;
    }

    .badge {
        font-size: 12px;
        color: #999;
    }
}
</style>
